﻿using System;
using System.Data;
using System.Data.Common;
using System.Data.OleDb;
using Ooaavee.Data.Model;

namespace Ooaavee.Data.Queries
{
    internal static class QueryFactory
    {
        internal static IQuery CreateDirectTableQuery(QueryContext context, Table table)
        {
            IQuery query = new DirectTableQuery(context, table, null);
            return query;
        }

        internal static IQuery CreateDirectTableQuery(QueryContext context, Table table, string customClause)
        {
            IQuery query = new DirectTableQuery(context, table, customClause);
            return query;
        }

        internal  static IQuery CreateChildTableQuery(Dependency dependency)
        {
            IQuery query = new ChildTableQuery(dependency);
            return query;
        }



        private abstract class Query : IQuery
        {
            protected Query(QueryContext context)
            {
                Context = context;
                Context.CancelQueryRequested += CancelQueryRequested;
            }

            public QueryContext Context { set; get; }

            public abstract QueryResult Execute();

            protected IQuery Parent { set; get; }

            protected OleDbCommand ActiveCommand { set; get; }

            protected bool IsCancelled { get; private set; }

            protected abstract string GenerateSql();

            protected QueryResult ReadFromDatabase(OleDbDataReader reader, Table table)
            {
                QueryResult result = new QueryResult { Parent = this, Table = table };

                using (reader)
                {
                    while (IsCancelled == false && reader.Read())
                    {
                        QueryResultRow row = new QueryResultRow { Parent = result };

                        foreach (Column column in table.Columns)
                        {
                            object dbValue = null;
                            if (reader.IsDBNull(column.OrdinalPosition - 1) == false)
                            {
                                dbValue = reader.GetValue(column.OrdinalPosition - 1);
                            }

                            row.Values.Add(new QueryResultRowValue { Column = column, Parent = row, Table = column.Parent, Value = dbValue });
                        }

                        result.Rows.Add(row);
                    }
                }

                return result;
            }

            protected OleDbParameter CreateParameter(QueryResultRowValue value)
            {
                OleDbParameter parameter = new OleDbParameter();
                parameter.ParameterName = SqlStatements.JoinParameterName;
                parameter.Direction = ParameterDirection.Input;
                parameter.Value = value.Value;

                /*
        protected SqlParameter CreateParameter(string paramName, object value, Column column) {
            SqlParameter param = new SqlParameter();

            param.ParameterName = paramName;
            param.SqlDbType = column.DataType.SqlServerSpecific;
            param.Direction = ParameterDirection.Input;
            param.Value = value;

            if (column.CharacterMaximumLength != null) {
                param.Size = Int32.MaxValue;
//                param.Size = column.CharacterMaximumLength.Value;
            }

            if (column.NumericPrecision != null) {
                param.Precision = (byte)column.NumericPrecision.Value;
            }

            if (column.NumericScale != null) {
                param.Scale = (byte)column.NumericScale.Value;
            }

            return param;
        }
                */



                throw new NotImplementedException();
            }

            private void CancelQueryRequested(object sender, EventArgs e)
            {
                if (ActiveCommand != null)
                {
                    IsCancelled = true;
                    ActiveCommand.Cancel();
                }
            }
        }




        private sealed class DirectTableQuery : Query
        {
            private readonly Table _table;
            private readonly string _customClause;

            internal DirectTableQuery(QueryContext context, Table table, string customClause) : base(context)
            {
                _table = table;
                _customClause = customClause;
            }

            protected override string GenerateSql()
            {
                return string.Format("{0} {1} {2}", 
                    SqlStatements.BrowseSelect(_table), 
                    SqlStatements.BrowseFrom(_table), 
                    _customClause);
            }

            public override QueryResult Execute()
            {
                using (var connection = Context.Connections.GetOleDbConnection())
                {
                    try
                    {
                        using (ActiveCommand = new OleDbCommand())
                        {
                            ActiveCommand.CommandText = GenerateSql();
                            ActiveCommand.CommandType = CommandType.Text;
                            ActiveCommand.Connection = connection;
                            ActiveCommand.Prepare();

                            return ReadFromDatabase(ActiveCommand.ExecuteReader(), _table);
                        }
                    }
                    catch (DbException ex)
                    {
                        if (IsCancelled == false)
                        {
                            Context.OnQueryErrorEvent(new QueryErrorEventArgs(ex));
                            throw;
                        }
                    }
                }

                return null;
            }
        }

        private sealed class ChildTableQuery : Query
        {
            private readonly Dependency _dependency;

            internal ChildTableQuery(Dependency dependency) : base(dependency.Parent.Parent.Parent.Context)
            {
                _dependency = dependency;
            }

            protected override string GenerateSql()
            {
                return string.Format("{0} {1} {2}", 
                    SqlStatements.BrowseSelect(_dependency.Column.Parent), 
                    SqlStatements.BrowseFrom(_dependency.Column.Parent), 
                    SqlStatements.BrowseWhere(_dependency.Column));
            }

            public override QueryResult Execute()
            {
                string sql = GenerateSql();

                OleDbParameter parameter = CreateParameter(null);

                
//                QueryResultValue paramValue = depending.Row[depending.Column.LinkedTo];


                using (var connection = Context.Connections.GetOleDbConnection())
                {
                    try
                    {
                        using (ActiveCommand = new OleDbCommand())
                        {
                            ActiveCommand.CommandText = sql;
                            ActiveCommand.CommandType = CommandType.Text;
                            ActiveCommand.Parameters.Add(parameter);
                            ActiveCommand.Connection = connection;
                            ActiveCommand.Prepare();
                            
//                            return ReadFromDatabase(ActiveCommand.ExecuteReader(), _table);
                        }
                    }
                    catch (DbException ex)
                    {
                        if (IsCancelled == false)
                        {
                            Context.OnQueryErrorEvent(new QueryErrorEventArgs(ex));
                            throw;
                        }
                    }
                }

                return null;

                /*
                QueryResults results = new QueryResults();
                QueryResultValue paramValue = depending.Row[depending.Column.LinkedTo];
                SqlParameter param;
                string sql;

                Handle.FireSubQueryStarted();
                sql = String.Format("{0} {1} {2}", SqlStatements.BrowseSelect(depending.Column.Table), SqlStatements.BrowseFrom(depending.Column.Table), SqlStatements.BrowseWhere(depending.Column));
                param = CreateParameter(SqlStatements.JOIN_PARAM_NAME, paramValue.Value, paramValue.Column);

                using (SqlConnection con = Handle.DataSource.GetSqlConnection())
                {
                    try
                    {
                        using (ActiveCommand = new SqlCommand())
                        {
                            ActiveCommand.CommandText = sql;
                            ActiveCommand.CommandType = CommandType.Text;
                            ActiveCommand.Parameters.Add(param);
                            ActiveCommand.Connection = con;
                            results.Add(ReadFromDatabase(depending.Column.Table, ActiveCommand));
                        }
                    }
                    catch (Exception e)
                    {
                        if (!Cancelled) throw e;
                    }
                }
                Handle.FireSubQueryCompleted();
                return results;
                */
                throw new NotImplementedException();
            }
        }









        private sealed class ParentTableQuery : Query
        {
            private QueryResultRowValue _foreignKeyValue;

            public ParentTableQuery(QueryResultRowValue foreignKeyValue)
                : base(foreignKeyValue.Parent.Parent.Parent.Context)
            {
                _foreignKeyValue = foreignKeyValue;
                Parent = foreignKeyValue.Parent.Parent.Parent;
            }

            protected override string GenerateSql()
            {
                throw new NotImplementedException();
            }

            public override QueryResult Execute()
            {
                throw new NotImplementedException();
            }
        }





    }
}
